package com.riwon.im.websocket;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.HandshakeData;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.annotation.OnConnect;
import com.corundumstudio.socketio.annotation.OnDisconnect;
import com.corundumstudio.socketio.annotation.OnEvent;
import com.riwon.im.common.bean.MsgCondition;
import com.riwon.im.common.constant.MsgConstant;
import com.riwon.im.common.constant.UserConstant;
import com.riwon.im.common.constant.UserConstant.State;
import com.riwon.im.common.constant.WebSocketConstant.Event;
import com.riwon.im.common.result.ResultData;
import com.riwon.im.pojo.Msg;
import com.riwon.im.pojo.User;
import com.riwon.im.service.IMsgService;
import com.riwon.im.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.websocket.OnError;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

import static com.riwon.im.common.result.CommonRespStatus.HAVE_NO_STAFF;
import static com.riwon.im.common.result.CommonRespStatus.NEED_REGISTER;

/**
 * 聊天服务
 * @author wpy
 * @date 2018/5/9 17:22
 */
@Component
public class ChatServer {
    private static final Logger LOGGER = LoggerFactory.getLogger(ChatServer.class);

    @Resource
    private SocketIOServer socketIOServer;
    @Resource
    private IMsgService msgServiceImpl;
    @Resource
    private IUserService userServiceImpl;

    /**
     * 打开连接
     * @param client
     */
    @OnConnect
    public void onConnect(SocketIOClient client) {
        HandshakeData handshakeData = client.getHandshakeData();
        String userId = handshakeData.getSingleUrlParam("userId");
        User user = userServiceImpl.selectById(Objects.isNull(userId) ? 0 : NumberUtils.createInteger(userId));
        if(user == null){
            client.sendEvent(Event.ERROR, ResultData.build().setStatus(NEED_REGISTER));
            client.disconnect();
            return;
        }
        //跟新用户为上线状态
        user.setState(State.ONLINE.getCode());
        user.setUpdateTime(new Date());
        user.setSocketUuid(client.getSessionId().toString());
        userServiceImpl.updateById(user);

        //如果时客户，获取接待客户的员工,发送给客户
        if(!UserConstant.Type.USER.equals(UserConstant.Type.getByCode(user.getType()))){
            String appid = handshakeData.getSingleUrlParam("appid");
            User staff = userServiceImpl.getNextStaff(user.getShopId());
            if(staff == null){
                client.sendEvent(Event.ERROR, ResultData.build().setStatus(HAVE_NO_STAFF));
                client.disconnect();
                return;
            }
            ResultData build = ResultData.build()
                    .put("staffId",staff.getId())
                    .put("userId",user.getId());
            client.sendEvent(Event.INFO, build);
        }
    }

    /**
     * 关闭连接
     * @param client
     */
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        HandshakeData handshakeData = client.getHandshakeData();
        String userId = handshakeData.getSingleUrlParam("userId");
        User user = userServiceImpl.selectById(NumberUtils.createInteger(userId));
        user.setSocketUuid("");
        user.setUpdateTime(new Date());
        user.setState(State.OFFLINE.getCode());
        userServiceImpl.updateAllColumnById(user);
    }

    /**
     * 接受消息事件
     * @param client
     * @param request
     * @param msg
     */
    @OnEvent(value = Event.MSG)
    public void onMessage(SocketIOClient client, AckRequest request,@Valid Msg msg) {
        Integer toId = msg.getToId();
        Integer fromId = msg.getFromId();
        User toUser = userServiceImpl.selectById(Objects.isNull(toId)?0:toId);
        User fromUser = userServiceImpl.selectById(Objects.isNull(fromId)?0:fromId);
        if(toUser == null){
            client.sendEvent(Event.ERROR,ResultData.build().parameterError().changeMsg("接收者不存在"));
            return;
        }
        //Msg
        msg.setCreateTime(new Date());
        msg.setUpdateTime(new Date());
        msg.setShopId(toUser.getShopId());
        msg.setAppid((msg.getUserId().equals(toUser.getId()))?toUser.getAppids():fromUser.getAppids());
        msg.setFromName(fromUser.getName());
        msg.setToName(toUser.getName());
        //接收者在线
        if(State.ONLINE.getCode() == toUser.getState()
                && StringUtils.isNotBlank(toUser.getSocketUuid())){
            msg.setStatus(MsgConstant.MsgState.SENT.getCode());
            msgServiceImpl.insert(msg);
            //发送消息，接受者和发送者
            SocketIOClient toClient = socketIOServer.getClient(UUID.fromString(toUser.getSocketUuid()));
            client.sendEvent(Event.MSG,msg);
            toClient.sendEvent(Event.MSG,msg);
            return;
        }
        //接收者离线
        msg.setStatus(MsgConstant.MsgState.NOT_SEND.getCode());
        msgServiceImpl.insert(msg);
        //发送者
        client.sendEvent(Event.MSG,msg);
    }

    /**
     * 查询聊天历史记录
     * @param client
     * @param request
     * @param condition
     */
    @OnEvent(value = Event.HISTORY)
    public void onHistory(SocketIOClient client, AckRequest request,@Valid MsgCondition condition){
        User user = userServiceImpl.selectById(condition.getUserId());
        if(user == null){
            client.sendEvent(Event.ERROR,ResultData.build().parameterError().changeMsg("用户不存在"));
            return;
        }
        Wrapper<Msg> wrapper = new EntityWrapper<>();
        wrapper.eq("user_id",user.getId());

        //获取未读消息,发送全部未读消息
        if(Objects.nonNull(condition.getToId()) && condition.isNotRead()){
            wrapper.eq("to_id",condition.getToId());
            wrapper.eq("state", MsgConstant.MsgState.NOT_SEND.getCode());
            List<Msg> msgList = msgServiceImpl.selectList(wrapper);
            client.sendEvent(Event.MSG,ResultData.build().parseList(msgList));
            return;
        }

        if(Objects.nonNull(condition.getStartOn())){
            wrapper.ge("create_time",condition.getStartOn());
        }
        if(Objects.nonNull(condition.getEndOn())){
            wrapper.le("create_time",condition.getStartOn());
        }
        Page<Msg> page = msgServiceImpl.selectPage(new Page<>(condition.getPageNum(), condition.getPageSize()), wrapper);
        client.sendEvent(Event.HISTORY,ResultData.build().parsePageBean(page));
    }

    /**
     * 消息已读
     * @param client
     * @param request
     * @param userId
     * @param msgIds
     */
    @OnEvent(value = Event.READ)
    public void onRead(SocketIOClient client, AckRequest request,Integer userId, List<Integer> msgIds){
        if(msgIds.isEmpty()){
            return;
        }
        List<Msg> msgList = msgServiceImpl.selectBatchIds(msgIds);
        //接受者是userId才是已读
        List<Msg> collect = msgList.stream()
                .filter(msg -> msg.getToId().equals(userId))
                .collect(Collectors.toList());
        collect.forEach(msg -> {
            msg.setUpdateTime(new Date());
            msg.setStatus(MsgConstant.MsgState.READ.getCode());
        });
        msgServiceImpl.updateBatchById(collect);
    }

    @OnError
    public void onError(SocketIOClient client,Exception e){
        LOGGER.error(e.getMessage(),e);
        client.sendEvent(Event.ERROR,e.getMessage());
    }
}
